The Python Data Science Stack


Presidents of the USA

The Python code below will load a dataset containing the names of the first 44 presidents of the USA and their heights, available in the file president_heights.csv, which is a simple comma-separated list of labels and values.

In [1]:
# Imports
import numpy as np
import pandas as pd
from pandas import DataFrame, Series

%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns

from scipy.stats import pearsonr
In [2]:
file = 'president_heights.csv'
presidents = pd.read_csv(file) 
presidents
Out[2]:
order name height(cm)
0 1 George Washington 189
1 2 John Adams 170
2 3 Thomas Jefferson 189
3 4 James Madison 163
4 5 James Monroe 183
5 6 John Quincy Adams 171
6 7 Andrew Jackson 185
7 8 Martin Van Buren 168
8 9 William Henry Harrison 173
9 10 John Tyler 183
10 11 James K. Polk 173
11 12 Zachary Taylor 173
12 13 Millard Fillmore 175
13 14 Franklin Pierce 178
14 15 James Buchanan 183
15 16 Abraham Lincoln 193
16 17 Andrew Johnson 178
17 18 Ulysses S. Grant 173
18 19 Rutherford B. Hayes 174
19 20 James A. Garfield 183
20 21 Chester A. Arthur 183
21 23 Benjamin Harrison 168
22 25 William McKinley 170
23 26 Theodore Roosevelt 178
24 27 William Howard Taft 182
25 28 Woodrow Wilson 180
26 29 Warren G. Harding 183
27 30 Calvin Coolidge 178
28 31 Herbert Hoover 182
29 32 Franklin D. Roosevelt 188
30 33 Harry S. Truman 175
31 34 Dwight D. Eisenhower 179
32 35 John F. Kennedy 183
33 36 Lyndon B. Johnson 193
34 37 Richard Nixon 182
35 38 Gerald Ford 183
36 39 Jimmy Carter 177
37 40 Ronald Reagan 185
38 41 George H. W. Bush 188
39 42 Bill Clinton 188
40 43 George W. Bush 182
41 44 Barack Obama 185

The below code will display the histogram of president's heights and compute summary statistics:

  • Mean height
  • Standard deviation
  • Minimum height, and
  • Maximum height.
In [3]:
height = np.array(presidents['height(cm)'])
plt.hist(height)
plt.title('Height Distribution')
plt.xlabel('Height')
plt.ylabel('Number of Presidents');
plt.show()
In [4]:
print("Height statistics")
print("Average height (global):", np.mean(height))
print("Standard Deviation of Height (global):", np.std(height))
print("Minimum height (global):", np.min(height))
print("Maximum height (global):", np.max(height))
Height statistics
Average height (global): 179.73809523809524
Standard Deviation of Height (global): 6.931843442745892
Minimum height (global): 163
Maximum height (global): 193

Nxt we will write Python code to answer the following questions:

  1. Who was(were) the tallest president(s)?
  2. Who was(were) the shortest president(s)?
  3. How many presidents were 6' tall or taller?
In [5]:
print("Tallest preisdent(s): ")
for i in range(0, len(height)):
    if height[i]==np.max(height):
        print(str(presidents["order"][i]) + "    " +presidents["name"][i]) 
Tallest preisdent(s): 
16    Abraham Lincoln
36    Lyndon B. Johnson
In [6]:
print("Shortest preisdent(s): ")
for i in range(0, len(height)):
    if height[i]==np.min(height):
        print(str(presidents["order"][i]) + "    " +presidents["name"][i]) 
Shortest preisdent(s): 
4    James Madison
In [7]:
height_in_ft=height/30.48

print("Number of Presidents over 6 feet tall: ", len([x for x in height_in_ft if x>=6]))
height
Number of Presidents over 6 feet tall:  18
Out[7]:
array([189, 170, 189, 163, 183, 171, 185, 168, 173, 183, 173, 173, 175,
       178, 183, 193, 178, 173, 174, 183, 183, 168, 170, 178, 182, 180,
       183, 178, 182, 188, 175, 179, 183, 193, 182, 183, 177, 185, 188,
       188, 182, 185], dtype=int64)

This is an extremely small, simple and manageable dataset.

Let's use it to prove a silly hypothesis, for example:

"H1: Even-numbered presidents are, in average, taller than odd-numbered ones."

In [8]:
odd_height=height[0::2]
even_height=height[1::2]

if np.mean(even_height)>np.mean(odd_height):
    H1=True
else:
    H1=False
    
H1
Out[8]:
False

Hypothesis H1 was refuted.


Next we will text Hypothesis H2:

H2: The first 22 presidents are, on average, shorter than the last 22.

In [9]:
first_22=height[0:21:1]
last_22=height[22:43:1]

if np.mean(first_22)<np.mean(last_22):
    H2=True
else:
    H2=False
    
H2
Out[9]:
True

Hypothesis H2 was confirmed by the data.

HR payroll

The Python code below will load a dataset containing the salaries and demographic data of more than 1000 employees of a hypothetical company, available in the file salaries.csv, which is a simple comma-separated list of labels and values.

In [10]:
salaries = pd.read_csv('salaries.csv') 
print(salaries.shape)
print(salaries.count())
(1192, 6)
earn      1192
height    1192
sex       1192
ed        1192
age       1192
race      1192
dtype: int64

earn= employee salary

height= employee height

sex= employee sex

ed= years of employee education

age= employee age

race= employee race


Let's explore the dataset by plotting some graphs and displaying summary statistics.

The code below should display:

  • Min, max, average, and median salary (global)
  • A histogram of salaries
  • A scatterplot correlating salaries and years of education
  • The (Pearson) correlation coefficient between the two variables.

This should help us get started.

In [11]:
salary = np.array(salaries['earn'])
print("Salary statistics")
print("Minimum salary (global):", np.min(salary))
print("Maximum salary (global):", np.max(salary))
print("Average salary (global):", np.mean(salary))
print("Median  salary (global):", np.median(salary))
Salary statistics
Minimum salary (global): 200.0
Maximum salary (global): 200000.0
Average salary (global): 23154.773489932886
Median  salary (global): 20000.0
In [12]:
plt.hist(salary)
plt.title('Salary Distribution')
plt.xlabel('Salary')
plt.ylabel('Number of Employees');
plt.show()
In [13]:
years = np.array(salaries['ed'])
plt.title('Salary vs. Education Level')
plt.xlabel('Salary')
plt.ylabel('Years of education');
plt.scatter(salary, years, alpha=0.5)
plt.show()
In [14]:
# Compute Pearson coefficient
from scipy.stats import pearsonr
corr, _ = pearsonr(salary,years)
print('Correlation coefficient: ',corr)
Correlation coefficient:  0.3399765246894847

The Pearson correlation coefficient (a value between -1 and 1) can be used to summarize the strength of the linear relationship between two data samples.

A simplified way to interpret the result is:

  • A value of 0 means no correlation
  • Values below -0.5 or above 0.5 indicates a notable (negative/positive) correlation

The code below should:

  1. Display the total headcount and the number (and %) of male and female employees.
  2. Compute and display the min, max, average, and median salary per gender.
  3. Display a boxplot that could provide insight into the gender inequality (if any is present) associated with the salaries in the company.
In [15]:
genders=np.array(salaries['sex'])

males=[]
females=[]

for g in genders:
    if g=='male':
        males.append(g)
    else:
        females.append(g)

males=np.array(males)
females=np.array(females)

total_headcount=len(males)+len(females)
print("number of males:", len(males), (len(males)/total_headcount)*100)
print("number of females:", len(females), (len(females)/total_headcount)*100)
number of males: 505 42.36577181208054
number of females: 687 57.63422818791947
In [16]:
male_salaries=[]
female_salaries=[]

for i in range(0, len(salaries)):
    if salaries['sex'][i]=='male':
        male_salaries.append(salaries['earn'][i])
    else:
        female_salaries.append(salaries['earn'][i])

male_salaries=np.array(male_salaries)
female_salaries=np.array(female_salaries)

print("Salary statistics per gender:")
print("Minimum salary (male):", np.min(male_salaries))
print("Maximum salary (male):", np.max(male_salaries))
print("Average salary (male):", np.mean(male_salaries))
print("Median  salary (male):", np.median(male_salaries))

print("Minimum salary (female):", np.min(female_salaries))
print("Maximum salary (female):", np.max(female_salaries))
print("Average salary (female):", np.mean(female_salaries))
print("Median  salary (female):", np.median(female_salaries))
Salary statistics per gender:
Minimum salary (male): 1000.0
Maximum salary (male): 200000.0
Average salary (male): 29786.130693069306
Median  salary (male): 25000.0
Minimum salary (female): 200.0
Maximum salary (female): 123000.0
Average salary (female): 18280.195050946142
Median  salary (female): 15000.0
In [17]:
salaries.boxplot(column='earn', by='sex')
plt.title("Salaries by Sex")
plt.suptitle('')
plt.show()

It is clear from the dual boxplot above that the male group generally earns significantly more than the female group.


As you can possibly tell by now, this dataset may help us test hypotheses and answer questions related to possible sources of inequality associated with the salary distribution: gender, age, race, height.

Let's assume, for the sake of argument, that the number of years of education should correlate well with a person's salary (this is clearly a weak argument and the plot and Pearson correlation coefficient computation above suggests that this is not the case) and that other suspiciously high (positive or negative) correlations could be interpreted as a sign of inequality.

At this point, we formulate 3 different hypotheses that might suggest that the salary distribution is biased by factors such as ageism.

Call these hypotheses H3, H4, and H5.

H3: Older employees (65 and older) make less on average than younger ones.

H4: Non-white employees make less on average than white employees.

H5: Shorter employees make less on average than taller ones.

Next we will write Python code to test hypotheses H3, H4, and H5 (and some text to explain whether they were confirmed or not).

In [18]:
young_salaries=[]
older_salaries=[]

for i in range(0, len(salaries)):
    if salaries['age'][i]<65:
        young_salaries.append(salaries['earn'][i])
    else:
        older_salaries.append(salaries['earn'][i])

young_salaries=np.array(young_salaries)
older_salaries=np.array(older_salaries)

if np.mean(older_salaries)<np.mean(young_salaries):
    H3=True
else:
    H3=False

print(H3)

plt.scatter(salaries['age'], salaries['earn'])
plt.title("Age vs. Salary")
plt.xlabel("Age(years)")
plt.ylabel("Salary")
plt.show()
True

Hypothesis H3 was confirmed. Senior employees (65 and older) make less on average than younger employees. However, examining the scatter plot above reveals that much of the lower paid employees are actually more on the younger side of the age range of this dataset, with the highest paying jobs concentrated around the middle. This makes intuitive sense as middle aged employees are often in the prime of their career.

In [19]:
white_salaries=[]
nwhite_salaries=[]

for i in range(0, len(salaries)):
    if salaries['race'][i]=='white':
        white_salaries.append(salaries['earn'][i])
    else:
        nwhite_salaries.append(salaries['earn'][i])

white_salaries=np.array(white_salaries)
nwhite_salaries=np.array(nwhite_salaries)

if np.mean(nwhite_salaries)<np.mean(white_salaries):
    H4=True
else:
    H4=False

print(H4)

salaries.boxplot(column='earn', by='race')
plt.title("Salaries by Race")
plt.suptitle('')
plt.show()
True

Hypothesis H4 was confirmed. Nonwhite employees make less on average than white employees. In addition, it is clear from the boxplots above that there is an inherent salary bias towards white employees in this company. Although there are much fewer non-white employees in this company, their salaries come nowhere near those of white employees.

In [20]:
short_salaries=[]
tall_salaries=[]

for i in range(0, len(salaries)):
    if salaries['height'][i]<66:
        short_salaries.append(salaries['earn'][i])
    else:
        tall_salaries.append(salaries['earn'][i])

short_salaries=np.array(short_salaries)
tall_salaries=np.array(tall_salaries)

if np.mean(short_salaries)<np.mean(tall_salaries):
    H5=True
else:
    H5=False

print(H5)

plt.scatter(salaries['height'], salaries['earn'])
plt.title("Height vs. Salary")
plt.xlabel("Height (inches)")
plt.ylabel("Salary")
plt.show()
True

Hypothesis H5 was confirmed. On average, shorter employees (shorter than 66 inches) make less than taller ones. In addition, an examination of the scatter plot above reveals that the highest paid employees are near the median value for height.

Fuel consumption

The Python code below will load a dataset containing fuel consumption data for ~400 vehicles produced in the 1970s and the 1980s along with some characteristic information associated with each model.

Here, displacement refers to a vehicle's engine size and the fuel efficiency is measured in miles per gallon (mpg).

See: https://archive.ics.uci.edu/ml/datasets/Auto+MPG for additional information.

In [21]:
sns.set(style='ticks', palette='Set2')
%matplotlib inline

data = pd.read_csv("http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data-original",
                   delim_whitespace = True, header=None,
                   names = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration',
                            'model', 'origin', 'car_name'])
print(data.shape)
(406, 9)
In [22]:
data.dropna(inplace=True)
data.head()
data.reset_index(drop=True, inplace=True)
data
Out[22]:
mpg cylinders displacement horsepower weight acceleration model origin car_name
0 18.0 8.0 307.0 130.0 3504.0 12.0 70.0 1.0 chevrolet chevelle malibu
1 15.0 8.0 350.0 165.0 3693.0 11.5 70.0 1.0 buick skylark 320
2 18.0 8.0 318.0 150.0 3436.0 11.0 70.0 1.0 plymouth satellite
3 16.0 8.0 304.0 150.0 3433.0 12.0 70.0 1.0 amc rebel sst
4 17.0 8.0 302.0 140.0 3449.0 10.5 70.0 1.0 ford torino
... ... ... ... ... ... ... ... ... ...
387 27.0 4.0 140.0 86.0 2790.0 15.6 82.0 1.0 ford mustang gl
388 44.0 4.0 97.0 52.0 2130.0 24.6 82.0 2.0 vw pickup
389 32.0 4.0 135.0 84.0 2295.0 11.6 82.0 1.0 dodge rampage
390 28.0 4.0 120.0 79.0 2625.0 18.6 82.0 1.0 ford ranger
391 31.0 4.0 119.0 82.0 2720.0 19.4 82.0 1.0 chevy s-10

392 rows × 9 columns

The code below should:

  1. Count the number of 3- and 5-cylinder vehicles in the dataset, display the count, and discard those entries (rows).
  2. Compute and display the min, max, and average fuel consumption (in mpg) for 4-, 6-, and 8-cylinder vehicles.
  3. Display the name of the most and least fuel efficient vehicles in the dataset.
In [23]:
cylinder_values=np.array(data['cylinders'])
cylinder_count=0

for i in range(0, len(cylinder_values)):
    if cylinder_values[i]==3.0 or cylinder_values[i]==5.0:
        cylinder_count=cylinder_count+1

print("Number of 3 and 5-cylinder vehicles: ", cylinder_count)

data=data.drop(data[(data.cylinders ==3.0) | (data.cylinders ==5.0)].index)
data
Number of 3 and 5-cylinder vehicles:  7
Out[23]:
mpg cylinders displacement horsepower weight acceleration model origin car_name
0 18.0 8.0 307.0 130.0 3504.0 12.0 70.0 1.0 chevrolet chevelle malibu
1 15.0 8.0 350.0 165.0 3693.0 11.5 70.0 1.0 buick skylark 320
2 18.0 8.0 318.0 150.0 3436.0 11.0 70.0 1.0 plymouth satellite
3 16.0 8.0 304.0 150.0 3433.0 12.0 70.0 1.0 amc rebel sst
4 17.0 8.0 302.0 140.0 3449.0 10.5 70.0 1.0 ford torino
... ... ... ... ... ... ... ... ... ...
387 27.0 4.0 140.0 86.0 2790.0 15.6 82.0 1.0 ford mustang gl
388 44.0 4.0 97.0 52.0 2130.0 24.6 82.0 2.0 vw pickup
389 32.0 4.0 135.0 84.0 2295.0 11.6 82.0 1.0 dodge rampage
390 28.0 4.0 120.0 79.0 2625.0 18.6 82.0 1.0 ford ranger
391 31.0 4.0 119.0 82.0 2720.0 19.4 82.0 1.0 chevy s-10

385 rows × 9 columns

In [24]:
fuel_consumption=np.array(data['mpg'])

fuel_max=np.max(fuel_consumption)
fuel_min=np.min(fuel_consumption)
fuel_avg=np.mean(fuel_consumption)

print("Maximum mpg: ", fuel_max)
print("Minimum mpg: ", fuel_min)
print("Average mpg: ", fuel_avg)
Maximum mpg:  46.6
Minimum mpg:  9.0
Average mpg:  23.445454545454545
In [25]:
print("Most fuel efficient vehicle(s): ")
for i in range(0, len(fuel_consumption)):
    if fuel_consumption[i]==np.max(fuel_consumption):
        print(data['car_name'][i]+ "\n") 

print("Least fuel efficient vehicle(s): ")
for i in range(0, len(fuel_consumption)):
    if fuel_consumption[i]==np.min(fuel_consumption):
        print(data['car_name'][i]+ "\n") 
Most fuel efficient vehicle(s): 
audi 4000

Least fuel efficient vehicle(s): 
hi 1200d


This dataset may help us test hypotheses and answer questions related to fuel consumption.

To get started: Which features of a vehicle correlate best with its mpg -- displacement, weight, or horsepower?

The Python code below should plot the relationship between:

  1. Fuel consumption and displacement (engine size)
  2. Fuel consumption and weight
  3. Fuel consumption and horsepower (HP)
In [26]:
plt.scatter(data['mpg'], data['displacement'])
plt.title("Fuel Consumption vs. Displacement")
plt.xlabel("Fuel consumption(mpg)")
plt.ylabel("Displacement")
plt.show()
In [27]:
plt.scatter(data['mpg'], data['weight'])
plt.title("Fuel Consumption vs. Weight")
plt.xlabel("Fuel consumption(mpg)")
plt.ylabel("Weight")
plt.show()
In [28]:
plt.scatter(data['mpg'], data['horsepower'])
plt.title("Fuel Consumption vs. Horsepower")
plt.xlabel("Fuel consumption(mpg)")
plt.ylabel("Horsepower")
plt.show()

There is a negative correlation between mpg and displacement. It appears to be linear.

There is a negative correlation between mpg and weight. It appears to be non-linear.

There is a negative correlation between mpg and horsepower. It appears to be non-linear.

Next we will write Python code to produce box plots that should provide good answers the questions below:

  1. Did vehicles get more efficient over the years (represented in this dataset, i.e., 1970 through 1982)?
  2. Are Japanese cars more fuel efficient than American or European ones?
In [29]:
data['Country_code'] = data.origin.replace([1,2,3],['USA','Europe','Japan'])
data.boxplot(column='mpg', by='model')
plt.title('')
plt.suptitle("Fuel efficiency by year")
plt.ylabel("Fuel consumption(mpg)")
plt.show()

data.boxplot(column='mpg', by='Country_code')
plt.title('')
plt.suptitle("Fuel efficency by countries")
plt.ylabel("Fuel consumption(mpg)")
plt.show()

The two boxplots above show that there is a genereal trend of increasing fuel efficiency as time moves forward.

In addition, it is evident that Japanese cars are generally more fuel efficient than American cars.